// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "cc/debug/debug_rect_history.h"

#include <stddef.h>

#include "base/memory/ptr_util.h"
#include "cc/base/math_util.h"
#include "cc/layers/layer_impl.h"
#include "cc/layers/layer_iterator.h"
#include "cc/layers/layer_list_iterator.h"
#include "cc/layers/layer_utils.h"
#include "cc/layers/render_surface_impl.h"
#include "cc/trees/damage_tracker.h"
#include "cc/trees/layer_tree_host.h"
#include "cc/trees/layer_tree_host_common.h"
#include "cc/trees/layer_tree_impl.h"
#include "ui/gfx/geometry/rect_conversions.h"

namespace cc {

// static
std::unique_ptr<DebugRectHistory> DebugRectHistory::Create()
{
    return base::WrapUnique(new DebugRectHistory());
}

DebugRectHistory::DebugRectHistory() { }

DebugRectHistory::~DebugRectHistory() { }

void DebugRectHistory::SaveDebugRectsForCurrentFrame(
    LayerTreeImpl* tree_impl,
    LayerImpl* hud_layer,
    const LayerImplList& render_surface_layer_list,
    const LayerTreeDebugState& debug_state)
{
    // For now, clear all rects from previous frames. In the future we may want to
    // store all debug rects for a history of many frames.
    debug_rects_.clear();

    if (debug_state.show_touch_event_handler_rects)
        SaveTouchEventHandlerRects(tree_impl);

    if (debug_state.show_wheel_event_handler_rects)
        SaveWheelEventHandlerRects(tree_impl);

    if (debug_state.show_scroll_event_handler_rects)
        SaveScrollEventHandlerRects(tree_impl);

    if (debug_state.show_non_fast_scrollable_rects)
        SaveNonFastScrollableRects(tree_impl);

    if (debug_state.show_paint_rects)
        SavePaintRects(tree_impl);

    if (debug_state.show_property_changed_rects)
        SavePropertyChangedRects(render_surface_layer_list, hud_layer);

    if (debug_state.show_surface_damage_rects)
        SaveSurfaceDamageRects(render_surface_layer_list);

    if (debug_state.show_screen_space_rects)
        SaveScreenSpaceRects(render_surface_layer_list);

    if (debug_state.show_layer_animation_bounds_rects)
        SaveLayerAnimationBoundsRects(render_surface_layer_list);
}

void DebugRectHistory::SavePaintRects(LayerTreeImpl* tree_impl)
{
    // We would like to visualize where any layer's paint rect (update rect) has
    // changed, regardless of whether this layer is skipped for actual drawing or
    // not. Therefore we traverse over all layers, not just the render surface
    // list.
    for (auto* layer : *tree_impl) {
        Region invalidation_region = layer->GetInvalidationRegionForDebugging();
        if (invalidation_region.IsEmpty() || !layer->DrawsContent())
            continue;

        for (Region::Iterator it(invalidation_region); it.has_rect(); it.next()) {
            debug_rects_.push_back(DebugRect(
                PAINT_RECT_TYPE, MathUtil::MapEnclosingClippedRect(layer->ScreenSpaceTransform(), it.rect())));
        }
    }
}

void DebugRectHistory::SavePropertyChangedRects(
    const LayerImplList& render_surface_layer_list,
    LayerImpl* hud_layer)
{
    for (size_t i = 0; i < render_surface_layer_list.size(); ++i) {
        size_t surface_index = render_surface_layer_list.size() - 1 - i;
        LayerImpl* render_surface_layer = render_surface_layer_list[surface_index];
        RenderSurfaceImpl* render_surface = render_surface_layer->render_surface();
        DCHECK(render_surface);

        const LayerImplList& layer_list = render_surface->layer_list();
        for (unsigned layer_index = 0;
             layer_index < layer_list.size();
             ++layer_index) {
            LayerImpl* layer = layer_list[layer_index];

            if (layer->render_surface() && layer->render_surface() != render_surface)
                continue;

            if (layer == hud_layer)
                continue;

            if (!layer->LayerPropertyChanged())
                continue;

            debug_rects_.push_back(DebugRect(
                PROPERTY_CHANGED_RECT_TYPE,
                MathUtil::MapEnclosingClippedRect(layer->ScreenSpaceTransform(),
                    gfx::Rect(layer->bounds()))));
        }
    }
}

void DebugRectHistory::SaveSurfaceDamageRects(
    const LayerImplList& render_surface_layer_list)
{
    for (size_t i = 0; i < render_surface_layer_list.size(); ++i) {
        size_t surface_index = render_surface_layer_list.size() - 1 - i;
        LayerImpl* render_surface_layer = render_surface_layer_list[surface_index];
        RenderSurfaceImpl* render_surface = render_surface_layer->render_surface();
        DCHECK(render_surface);

        debug_rects_.push_back(DebugRect(
            SURFACE_DAMAGE_RECT_TYPE, MathUtil::MapEnclosingClippedRect(render_surface->screen_space_transform(), render_surface->GetDamageRect())));
    }
}

void DebugRectHistory::SaveScreenSpaceRects(
    const LayerImplList& render_surface_layer_list)
{
    for (size_t i = 0; i < render_surface_layer_list.size(); ++i) {
        size_t surface_index = render_surface_layer_list.size() - 1 - i;
        LayerImpl* render_surface_layer = render_surface_layer_list[surface_index];
        RenderSurfaceImpl* render_surface = render_surface_layer->render_surface();
        DCHECK(render_surface);

        debug_rects_.push_back(
            DebugRect(SCREEN_SPACE_RECT_TYPE,
                MathUtil::MapEnclosingClippedRect(
                    render_surface->screen_space_transform(),
                    render_surface->content_rect())));
    }
}

void DebugRectHistory::SaveTouchEventHandlerRects(LayerTreeImpl* tree_impl)
{
    LayerTreeHostCommon::CallFunctionForEveryLayer(
        tree_impl,
        [this](LayerImpl* layer) { SaveTouchEventHandlerRectsCallback(layer); });
}

void DebugRectHistory::SaveTouchEventHandlerRectsCallback(LayerImpl* layer)
{
    for (Region::Iterator iter(layer->touch_event_handler_region());
         iter.has_rect();
         iter.next()) {
        debug_rects_.push_back(
            DebugRect(TOUCH_EVENT_HANDLER_RECT_TYPE,
                MathUtil::MapEnclosingClippedRect(
                    layer->ScreenSpaceTransform(), iter.rect())));
    }
}

void DebugRectHistory::SaveWheelEventHandlerRects(LayerTreeImpl* tree_impl)
{
    EventListenerProperties event_properties = tree_impl->event_listener_properties(EventListenerClass::kMouseWheel);
    if (event_properties == EventListenerProperties::kNone || event_properties == EventListenerProperties::kPassive) {
        return;
    }

    // Since the wheel event handlers property is on the entire layer tree just
    // mark inner viewport if have listeners.
    LayerImpl* inner_viewport = tree_impl->InnerViewportScrollLayer();
    if (!inner_viewport)
        return;
    debug_rects_.push_back(DebugRect(
        WHEEL_EVENT_HANDLER_RECT_TYPE,
        MathUtil::MapEnclosingClippedRect(inner_viewport->ScreenSpaceTransform(),
            gfx::Rect(inner_viewport->bounds()))));
}

void DebugRectHistory::SaveScrollEventHandlerRects(LayerTreeImpl* tree_impl)
{
    LayerTreeHostCommon::CallFunctionForEveryLayer(
        tree_impl,
        [this](LayerImpl* layer) { SaveScrollEventHandlerRectsCallback(layer); });
}

void DebugRectHistory::SaveScrollEventHandlerRectsCallback(LayerImpl* layer)
{
    if (!layer->layer_tree_impl()->have_scroll_event_handlers())
        return;

    debug_rects_.push_back(
        DebugRect(SCROLL_EVENT_HANDLER_RECT_TYPE,
            MathUtil::MapEnclosingClippedRect(layer->ScreenSpaceTransform(),
                gfx::Rect(layer->bounds()))));
}

void DebugRectHistory::SaveNonFastScrollableRects(LayerTreeImpl* tree_impl)
{
    LayerTreeHostCommon::CallFunctionForEveryLayer(
        tree_impl,
        [this](LayerImpl* layer) { SaveNonFastScrollableRectsCallback(layer); });
}

void DebugRectHistory::SaveNonFastScrollableRectsCallback(LayerImpl* layer)
{
    for (Region::Iterator iter(layer->non_fast_scrollable_region());
         iter.has_rect();
         iter.next()) {
        debug_rects_.push_back(
            DebugRect(NON_FAST_SCROLLABLE_RECT_TYPE,
                MathUtil::MapEnclosingClippedRect(
                    layer->ScreenSpaceTransform(), iter.rect())));
    }
}

void DebugRectHistory::SaveLayerAnimationBoundsRects(
    const LayerImplList& render_surface_layer_list)
{
    LayerIterator end = LayerIterator::End(&render_surface_layer_list);
    for (LayerIterator it = LayerIterator::Begin(&render_surface_layer_list);
         it != end; ++it) {
        if (!it.represents_itself())
            continue;

        // TODO(avallee): Figure out if we should show something for a layer who's
        // animating bounds but that we can't compute them.
        gfx::BoxF inflated_bounds;
        if (!LayerUtils::GetAnimationBounds(**it, &inflated_bounds))
            continue;

        debug_rects_.push_back(
            DebugRect(ANIMATION_BOUNDS_RECT_TYPE,
                gfx::ToEnclosingRect(gfx::RectF(inflated_bounds.x(),
                    inflated_bounds.y(),
                    inflated_bounds.width(),
                    inflated_bounds.height()))));
    }
}

} // namespace cc
